#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <queue>
#include <functional>
#include "log.hpp"
#include <assert.h>

// std::function<void*(void*)> func;
log lg;
int defaultnums = 5;

template <class T>
class threadpool
{
private:
    static_assert(std::is_invocable_r<void, T>::value, "T must be callable and return void");

    static void *routine(void *Argv)
    {
        threadpool<T> *pt = static_cast<threadpool<T> *>(Argv);
        while (1)
        {
            pthread_mutex_lock(&pt->_mutex);
            while (pt->_q.empty())
            {
                pthread_cond_wait(&pt->_cond, &pt->_mutex);
            }
            // pthread_cond_signal(&_cond);
            T t = pt->_q.front();
            pt->_q.pop();
            pthread_mutex_unlock(&pt->_mutex);
            std::cout << "get message success " << std::endl;
            t();
        }
        return nullptr;
    }

    threadpool(int size = defaultnums)
        : _size(size), _vpt(_size)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~threadpool()
    {
        for (auto pid : _vpt)
        {
            pthread_join(pid, nullptr);
        }
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    threadpool(const threadpool<T> &) = delete;
    const threadpool<T> &operator=(const threadpool<T> &) = delete;

public:
    bool Init()
    {
        if (_isinit)
            return false;
        _isinit = true;
        int cut = _size;
        while (cut--)
        {
            pthread_t pt;
            if (pthread_create(&pt, nullptr, routine, this) == 0)
            {
                _vpt.push_back(pt);
            }
            else
            {
                lg(Error, "thread create failed");
            }
        }
        return true;
    }

    inline bool IsInit() { return _isinit; }

    void push(T t)
    {
        pthread_mutex_lock(&_mutex);
        _q.push(t);
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mutex);
    }

    // void operator()(T t)
    // {
    //     push(t);
    // }

    static threadpool<T> *GetInstance()
    {
        // static_assert(std::is_invocable<T>::value, "T must be a callable type");
        if (_tp == nullptr)
        {
            pthread_mutex_lock(&_instance);
            if (_tp == nullptr)
            {
                _tp = new threadpool<T>();
                std::cout << "thread create success!!!" << std::endl;
            }
            pthread_mutex_unlock(&_instance);
        }

        return _tp;
    }

private:
    std::vector<pthread_t> _vpt;
    std::queue<T> _q;
    int _size;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    bool _isinit = false;

    // 单例模式/懒汉
    static threadpool<T> *_tp;
    static pthread_mutex_t _instance;
};

template <class T>
threadpool<T> *threadpool<T>::_tp = nullptr;

template <class T>
pthread_mutex_t threadpool<T>::_instance = PTHREAD_MUTEX_INITIALIZER;